在閱讀經典書籍《設計模式 Design Patterns》中,如果你跳過第 1 章引言,那你可能就錯過了設計模式的核心概念了!作者在引言中花了大篇幅講解 介面 在物件導向設計中的定位,以及設計模式如何透過 介面 來解決問題。
介面 可以說是物件導向設計用來處理 軟體複雜度 的利器,因此本文將會講解 介面 在物件導向設計中所扮演的角色為何,以及實務開發中會如何利用介面讓程式碼適應變化!
類別(Class)可以宣告的公開方法(Public Method),供使用者從外部操作類別的功能。類別的所有公開方法集合起來被稱為「類別的介面」。
介面是物件導向的基本元素,外部環境只能透過介面向類別請求執行功能或存取類別內部的資源。若沒有介面,類別將沒辦法與外部環境互動,外部環境也沒辦法得知類別的內容。
介面的存在,造就了 外部視角與內部視角。介面暴露了類別的對外公開資訊形成外部視角。外部環境只能從外部視角觀察一個類別的基本行為,無法得知類別內部(內部視角)是如何實現介面的行為。外部環境只關注介面的特性,使得介面可以和實現內容分離。因此,一個介面可以被多個不同的類別實現;實現相同介面的兩個類別,可以對介面實現完全不同的行為。
雖然說介面與實現內容是分離的,但程式碼運行中,介面的行為是由具體的實現內容組成。向一個介面調用同一個方法,會因為不同的具體實現而有不同的行為。程式碼有辦法在調用介面的瞬間,連接到介面當下的具體實現,這個能力被稱為動態綁定(Dynamic binding)。
動態綁定讓程式碼能在調用介面的瞬間才受到具體實現的影響,這種彈性讓開發人員有辦法在系統運行的過程中,動態替換相同介面的具體實現。這種可替換性就被稱為 多型(Polymorphism)。
以上面的購物車線上付費功能(Pay)為例,若不使用多型來實踐,則所有付款商的連線(connect)、購物(purchase)與付款(send)的實現細節,都將暴露在購物車的付費程式碼中。屆時,程式碼不但變得很長,還會充滿一堆 switch case 或 if else,最慘的是付款商之間的程式碼互相耦合了!一但程式碼依賴實現內容就會出現很多問題,維護起來會變得困難。這也是為什麼,設計模式的作者呼籲開發人員應該要:
Program to an interface, not an implementation.
讓程式碼關注於介面,而不是實現內容。
開發人員應該要 利用介面來對實現細節進行抽象簡化,將實作細節隱藏在類別的內部。並且利用介面動態替換具體實現的機制(多型),讓程式碼變得容易擴充與適應變化。
當然,需求在一開始的分析階段並不知道會有這麼多種細節,但需求會隨著時間不斷變化和增長。因此程式碼的設計必須隨著需求異動而改變,這也是《設計模式》的目的:提供一系列的良好的「設計」,使程式碼易於管理,亦是系統重構的目標。當系統變得越來越複雜時,開發人員可以不斷地透過重構調整程式碼的「設計」,讓程式碼適應變化!
註:上面購物車付費功能是引入 State 模式 來管理程式碼。